import commonMixin from '../mixins/common.js'

import CM from '@/class/index.js'
import utils from '@/utils/index.js'

// 滑动动画默认持续时间
const ANI_DURATION = 150
// 滑动行为名称
const SWIPER_ACTIONS = {
  swipeNext: 'swipe-next',
  swipePrev: 'swipe-prev',
  setNext: 'set-next',
  setPrev: 'set-prev'
}

export default {
  name: 'cm-calendar',
  mixins: [
    commonMixin
  ],
  props: {
    /* 
      初始化的日期的设定
      可以用于初始化CMDate对象即可
    */
    dateInit: {
      type: Object,
      default: null
    },
    // 是显示周历还是月历
    type: {
      default: 'month',
      type: String,
      validator (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['month', 'week'].indexOf(value) !== -1
      }
    },
    // 动画持续时间
    aniDuration: {
      default: ANI_DURATION,
      type: Number
    }
  },
  data () {
    return {
      // 选中的日期
      date: null,
      // 当前swiper真正的索引
      curIndex: 1,
      // 控制swiper动画的索引
      swiperIndex: 1,
      // 滑动组件当前的滑动行为
      swiperAction: '',
      // 月历列表，0 1 2 分别对应三个swiper-item
      dateSet: [[], [], []],
    }
  },
  computed: {
    // 选中的日期
    selectedIndex () {
      if (!this.date) { return -1 }
      const index = this.dateSet[this.curIndex].findIndex(item => item.rulue === this.date.rulue)
      return index
    },
    
    // 前一个swiper索引
    prevIndex () {
      if (this.curIndex === 0) {
        return 2
      }
      return this.curIndex - 1
    },
    
    // 后一个swiper索引
    nextIndex () {
      if (this.curIndex === 2) {
        return 0
      }
      return this.curIndex + 1
    },
    
    // 当前月
    curMonth () {
      if (!this.date) { return {} }
      return {
        year: this.date.year,
        month: this.date.month
      }
    },
    
    // 前一个月
    prevMonth () {
      if (typeof this.curMonth.year === 'number' && typeof this.curMonth.month === 'number') {
        return CM.Date.getDesMonth(this.curMonth.year, this.curMonth.month, -1)
      }
      return {}
    },
    
    // 后一个月
    nextMonth () {
      if (typeof this.curMonth.year === 'number' && typeof this.curMonth.month === 'number') {
        return CM.Date.getDesMonth(this.curMonth.year, this.curMonth.month, 1)
      }
      return {}
    }
  },
  methods: {
    // 初始化，isEventActive指定事件是否要触发
    init (date, isEventActive = true) {
      // 初始化选中日期
      this.date = new CM.Date(date)
      if (!this.date.enabled) {
        // 初始化的日期不合法，用当前的时间
        this.date = new CM.Date()
      }
      
      // 初始化日历数据
      this.dataSet = [[], [], []]
      const data = this.type === 'month' ?
        CM.Date.getCalendarMonthData(this.curMonth.year, this.curMonth.month) :  
        CM.Date.getCalendarWeekData(this.curMonth.year, this.curMonth.month, this.date.day)   
      this.$set(this.dateSet, this.prevIndex, data[0])
      this.$set(this.dateSet, this.curIndex, data[1])
      this.$set(this.dateSet, this.nextIndex, data[2])    
      
      // 初始化完成，触发一次事件
      if (isEventActive) {
        this.onDateChange()
      }
    },
    
    // 选中日期发生变动时触发
    onDateChange () {
      this.$emit('date-change', this.date, this.type)
    },
    
    // 点击日期触发
    dayClickHandler: utils.common.throttle(function (day) {
      this.gotoDay(day.year, day.month, day.day)
    }, ANI_DURATION),
    
    // 设置时间
    gotoTime (hour, min) {
      const options = {
        year: this.date.year,
        month: this.date.month,
        day: this.date.day,
        hour, min
      }
      const newDate = new CM.Date(options)
      if (!newDate.enabled) {
        // 目标日期不合法，报错
        this.$u.msg.toast('不合法的日期')
        return
      }
      // 时间更新
      this.date = newDate
    },
    
    // 选择某日
    gotoDay (year, month, day) {
      if (this.swiperAction !== '') {
        // 当swiper正在进行其他行为时，直接跳出
        return
      }
      
      const options = {
        year, month, day,
        hour: this.date.hour,
        min: this.date.min
      }
      const newDate = new CM.Date(options)
      if (!newDate.enabled) {
        // 目标日期不合法，报错
        this.$u.msg.toast('不合法的日期')
        return
      }
      
      // 判断滑动行为
      if (this.type === 'month') {
        if (year > this.date.year || (year === this.date.year && month > this.date.month)) {
          // 选择的日期在当月之后的月份
          this.swiperAction = SWIPER_ACTIONS.setNext
        }
        else if (year < this.date.year || (year === this.date.year && month < this.date.month)) {
          // 选择的日期在当月之前的月份
          this.swiperAction = SWIPER_ACTIONS.setPrev
        }
      }
      else {
        const weekInDay = CM.Date.config.DAY_IN_WEEK 
        // 保存变更前的日期
        const tempThisWeek = {
          start: this.dateSet[this.curIndex][0].rulue,
          end: this.dateSet[this.curIndex][weekInDay - 1].rulue
        }
        if (newDate.rulue > tempThisWeek.end) {
          // 选择的日期在当周之后的周
          this.swiperAction = SWIPER_ACTIONS.setNext
        }
        else if (newDate.rulue < tempThisWeek.start) {
          // 选择的日期在当周之前的周
          this.swiperAction = SWIPER_ACTIONS.setPrev
        }
      }
      
      // 执行滑动操作
      if (this.swiperAction === '') {
        // 没有变化，说明选中日期在本月
        this.date = newDate
        this.onDateChange()
      }
      else if (this.swiperAction === SWIPER_ACTIONS.setNext) {
        // 滑动前填充之后的数据
        this.setCalendarData(year, month, day, 'next', 0)
        // 缓存日期，之后滑动
        this.tempDate = newDate
        this.swipe(1)
      }
      else {
        // 滑动前填充之前的数据
        this.setCalendarData(year, month, day, 'prev', 0)
        // 缓存日期，之后滑动
        this.tempDate = newDate
        this.swipe(-1)
      }
    },
    
    // 手势滑动触发事件
    swipeHandler (e) {
      if (this.swiperAction !== '') {
        // 当swiper正在进行其他行为时，直接跳出
        return
      }
      console.log(e)
      
      const index = e.detail.current
      
      // 判断滑动行为
      if (index === this.nextIndex) {
        this.swiperAction = SWIPER_ACTIONS.swipeNext
      }
      else {
        this.swiperAction = SWIPER_ACTIONS.swipePrev
      }
      
      // swiper控制器跟进，使得手势滑动和代码控制的滑动不再有区别
      this.swiperIndex = index
    },
    
    // 动画结束触发事件
    animationFinishHandler (e) {
      if (this.swiperAction === '') {
        // 只有swiper被激活，才有可能触发动画
        return
      }
      
      const index = e.detail.current
      
      // 索引跟进
      this.curIndex = index
      // 日期跟进
      if (this.swiperAction === SWIPER_ACTIONS.setNext || this.swiperAction === SWIPER_ACTIONS.setPrev) {
        // 若是代码设置的滑动，从缓存中读取新日期
        this.date = this.tempDate
      }
      else {
        // 否则通过计算获取新日期
        this.updateAfterSwipe()
      }
      // 触发日期变动
      this.onDateChange()
      
      const { year, month, day } = this.date
      // 判断滑动行为，补充数据
      if (this.swiperAction === SWIPER_ACTIONS.setNext || this.swiperAction === SWIPER_ACTIONS.setPrev) {
        // 代码触发滑动，滑动完成后更新前后数据
        if (this.type === 'month') {
          this.setCalendarData(this.nextMonth.year, this.nextMonth.month, day, 'next')
          this.setCalendarData(this.prevMonth.year, this.prevMonth.month, day, 'prev')
        }
        else {
          this.setCalendarData(year, month, day, 'next', 1)
          this.setCalendarData(year, month, day, 'prev', -1)
        }
      }
      else if (this.swiperAction === SWIPER_ACTIONS.swipeNext) {
        // 向前滑动，则补充前面数据
        if (this.type === 'month') {
          this.setCalendarData(this.nextMonth.year, this.nextMonth.month, day, 'next')
        }
        else {
          this.setCalendarData(year, month, day, 'next', 1)
        }
      }
      else {
        // 向后滑动，则补充后面数据
        if (this.type === 'month') {
          this.setCalendarData(this.prevMonth.year, this.prevMonth.month, day, 'prev')
        }
        else {
          this.setCalendarData(year, month, day, 'prev', -1)
        }
      }
      // 一切完成后，swiper行为重置
      this.swiperAction = ''
    },
    
    /* 
      设置日历，
      mode为prev代表设置前一个swiper，mode为next代表设置后一个swiper
      offset 指定周历的偏移量，为1时，在year,month,day的基础上向前一周，为-1向后一周，为0本周
    */
    setCalendarData (year, month, day, mode = 'prev', offset = 0) {
      let data = null
      if (this.type === 'month') {
        data = CM.Date.getCalendarMonth(year, month)
      }
      else {
        data = CM.Date.getCalendarWeek(year, month, day, offset)
      }
      if (mode === 'next') {
        this.$set(this.dateSet, this.nextIndex, data)
      }
      else {
        this.$set(this.dateSet, this.prevIndex, data)
      }
    },
    
    // 手势滑动swiper后，获取新日期
    updateAfterSwipe () {
      if (this.swiperAction !== SWIPER_ACTIONS.swipePrev && this.swiperAction !== SWIPER_ACTIONS.swipeNext) {
        // 双保险,当手操滑动对象时，不允许更新数据
        return
      }
      
      // 日期跟进
      if (this.type === 'month') {
        // 月日期跟进
        const tempDate = this.dateSet[this.curIndex][CM.Date.config.DAY_IN_WEEK]
        let options = {
          year: tempDate.year,
          month: tempDate.month,
          day: this.date.day,
          hour: this.date.hour,
          min: this.date.min
        }
        let newDate = new CM.Date(options)
        while (!newDate.enabled && options.day > 0) {
          // 遇上无效的，不存在的日期，要向前追溯
          options.day--
          newDate = new CM.Date(options)
        }
        this.date = newDate            
      }
      else {
        // 周日期跟进
        const weekInDay = CM.Date.config.DAY_IN_WEEK
        const startRulue = this.dateSet[this.curIndex][0].rulue
        const desRulue = startRulue + this.date.weekDay
        let options = {
          rulue: desRulue,
          hour: this.date.hour,
          min: this.date.min
        }       
        this.date = new CM.Date(options)           
      }
    },
    
    // 代码控制滑动组件滑动
    swipe (offset = 1) {
      this.swiperIndex = (this.swiperIndex + 3 + (offset % 3)) % 3
    }
  },
  created () {
    this.init(this.dateInit)
  }
}